package org.example.study11.deadlyembrace;
// 经典死锁
import java.util.concurrent.TimeUnit;

public class DeadlockDemo {
    public static void main(String[] args) {
        // 创建两个锁对象作为共享资源
        // 注意：这两个对象将作为监视器锁（monitor lock）
        Object locker1 = new Object();
        Object locker2 = new Object();

        // 线程1：先获取locker1，再获取locker2
        Thread t1 = new Thread(() -> {
            System.out.println("t1: 开始申请locker1锁...");
            // 获取locker1的监视器锁
            synchronized (locker1) {
                System.out.println("t1: 成功获取locker1锁");

                // 模拟业务处理时间，增加死锁发生概率
                // 这个sleep让线程1有足够时间去获取第一个锁，
                // 同时给线程2时间去获取第二个锁
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("t1: 业务处理完成，准备获取locker2锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 在持有locker1的情况下请求locker2
                // 此时如果locker2已被线程2持有，就会发生死锁
                synchronized (locker2) {
                    System.out.println("t1: 成功获取所有锁资源，执行关键业务");
                }
            }
        });

        // 线程2：先获取locker2，再获取locker1
        Thread t2 = new Thread(() -> {
            System.out.println("t2: 开始申请locker2锁...");
            // 获取locker2的监视器锁
            synchronized (locker2) {
                System.out.println("t2: 成功获取locker2锁");

                // 同样的sleep，确保两个线程都能获得各自的第一个锁
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("t2: 业务处理完成，准备获取locker1锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 在持有locker2的情况下请求locker1
                // 此时如果locker1已被线程1持有，就会发生死锁
                synchronized (locker1) {
                    System.out.println("t2: 成功获取所有锁资源，执行关键业务");
                }
            }
        });

        // 启动两个线程
        // 由于线程调度和时序问题，死锁可能不会100%发生，但概率很高
        t1.start();
        t2.start();

        // 等待线程结束（实际上会因为死锁而永远等不到）
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
